namespace cxxcall
{

void av_data::_Reg_struct_ret ()
{
  if (this->rsize <= 16)
    this->flags |= reg_struct_ret;
}

bool av_data::arg_t (intptr_t val)
{
  if (this->ianum < IARG_NUM)
    {
      this->iargs[this->ianum++] = val;
      return (true);
    }
  else if (this->curp >= this->endp)
    return (this->set_overflow ());

  *this->curp++ = val;
  return (true);
}

bool av_data::arg (float val)
{
  if (this->fanum < FARG_NUM)
    {
      this->farg_types[this->fanum] = type::float_t;
      this->fargs[this->fanum++].f = val;
      return (true);
    }
  else if (this->curp >= this->endp)
    return (this->set_overflow ());

  *this->curp = 0;
  memcpy (this->curp++, &val, sizeof (val));
  return (true);
}

bool av_data::arg (double val)
{
  if (this->fanum < FARG_NUM)
    {
      this->farg_types[this->fanum] = type::double_t;
      this->fargs[this->fanum++].d = val;
      return (true);
    }
  else if (this->curp >= this->endp)
    return (this->set_overflow ());

  memcpy (this->curp, &val, sizeof (val));
  this->curp += sizeof (val) / WSIZE;
  return (true);
}

bool av_data::arg (long double val)
{
  if (this->fanum < FARG_NUM)
    {
      this->farg_types[this->fanum] = type::ldouble_t;
      this->fargs[this->fanum++].ld = val;
      return (true);
    }
  else if (this->curp + sizeof (val) / WSIZE > this->endp)
    return (this->set_overflow ());

  memcpy (this->curp, &val, sizeof (val));
  this->curp += sizeof (val) / WSIZE;
  return (true);
}

bool av_data::arg (const void *ptr, size_t size, size_t align)
{
  if (size <= 16)
    {
      if (((this->ianum * WSIZE + size + align - 1) & -(intptr_t)align) <=
          IARG_NUM * WSIZE)
        {
          memcpy (&this->iargs[this->ianum], ptr, size);
          this->ianum += ((((size + align - 1) & -(intptr_t)align) +
            WSIZE - 1) & -(intptr_t)WSIZE) / WSIZE;
        }
      else
        {
          auto ep = (uintptr_t *)(((uintptr_t)this->curp +
            size + align - 1) & -(intptr_t)align);

          if (ep > this->endp)
            return (this->set_overflow ());

          this->ianum = IARG_NUM;
          memcpy ((char *)ep - size, ptr, size);
          this->curp = (uintptr_t *)(((uintptr_t)ep +
            WSIZE - 1) & -(intptr_t)WSIZE);
        }
    }
  else
    {
      bool use_reg = this->ianum <= IARG_NUM;
      auto ep = (uintptr_t *)((uintptr_t)this->endp - (size + 7) & -8);

      if (use_reg ? this->curp > ep : this->curp >= ep)
        return (this->set_overflow ());

      memcpy (this->endp = ep, ptr, size);
      if (use_reg)
        this->iargs[this->ianum++] = (uintptr_t)ep;
      else
        *this->curp++ = (uintptr_t)ep;
    }
  
  return (true);
}

} // namespace cxxcall
